use crate::entity::Page;
use crate::service::DATE_FORMAT;
use crate::vo::sys_menu_vo::MenuVo;
use crate::SQLX;
use anyhow::anyhow;
use chrono::Local;
use sqlx::sqlite::SqliteQueryResult;
use sqlx::{FromRow, Sqlite};

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, FromRow)]
pub struct SysMenu {
    pub menu_id: Option<i64>,
    pub parent_id: Option<i64>,
    pub menu_name: Option<String>,
    pub permission: Option<String>,
    pub path: Option<String>,
    pub create_time: Option<String>,
    pub url: Option<String>,
    pub r#type: Option<u8>,
}

impl SysMenu {
    pub async fn add_menu(self) -> anyhow::Result<SqliteQueryResult> {
        let sqlx = SQLX.get().ok_or(anyhow!("db error"))?;
        let mut db = sqlx.acquire().await?;
        let format = Local::now().format(DATE_FORMAT).to_string();
        let result = sqlx::query(r#"INSERT INTO sys_menu(parent_id,menu_name,permission,path,create_time)VALUES(?,?,?,?,?)"#)
            .bind(self.parent_id)
            .bind(self.menu_name)
            .bind(self.permission)
            .bind(self.path)
            .bind(format).execute(&mut *db).await?;
        Ok(result)
    }
}

impl SysMenu {
    pub async fn query_page() -> anyhow::Result<Page<SysMenu>> {
        todo!()
    }
    pub async fn query_menus(role_id: Option<i64>) -> anyhow::Result<Option<Vec<MenuVo>>> {
        let db = SQLX.get().ok_or(anyhow::anyhow!("db error"))?;
        let result = sqlx::query_as::<Sqlite, SysMenu>(
            r#"select a.*
                 from sys_menu a left join sys_role_menu b on a.menu_id = b.menu_id
                 where b.role_id = ? order by parent_id asc "#)
            .bind(role_id)
            .fetch_all(db).await?;
        let tree = build_menu_tree(result).await;
        Ok(Some(tree))
    }
}

async fn build_menu_tree(mut menu_items: Vec<SysMenu>) -> Vec<MenuVo> {
    let mut tree: Vec<MenuVo> = Vec::new();
    fn build_tree(menu_items: &mut Vec<SysMenu>, parent_id: Option<i64>) -> Vec<MenuVo> {
        let mut subtree: Vec<MenuVo> = Vec::new();
        let mut i = 0;
        while i < menu_items.len() {
            if menu_items[i].parent_id == parent_id {
                let menu = menu_items.remove(i);
                let children = build_tree(menu_items, menu.menu_id);
                let node = MenuVo {
                    id: menu.menu_id,
                    parent_id: menu.parent_id,
                    url: menu.url,
                    name: menu.menu_name,
                    path: menu.path,
                    r#type: menu.r#type,
                    children,
                };
                subtree.push(node);
            } else {
                i += 1;
            }
        }
        subtree
    }

    // Build the entire tree starting with root nodes (parent_id is None)
    tree.append(&mut build_tree(&mut menu_items, Some(0)));

    tree
}